package com.jse.web;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.script.CompiledScript;

import com.jse.Fs;
import com.jse.IP;
import com.jse.Js;
import com.jse.Jse;
import com.jse.Lang;
import com.jse.Log;
import com.jse.Strings;
import com.jse.Three;
import com.jse.Tuple;

public class Web {
	
	public static ConcurrentHashMap<String,Object> SESSIONS=new ConcurrentHashMap<>();
	private final static Log log=Log.get("web");

	public final static Set<String> STATIC_PRIFIX=Lang.ofSet("/favicon.ico","/upload/","/assets/","/image/","/img/","/js/","/css/",
			"/font/","/fonts/","/public/","/static/","/404.html","/403.html","/500.html");
	public final static Set<String> STATIC_SUFFIX=Lang.ofSet("ico","css","js","htm","txt","mjs","xml",
			"jpg","png","jpeg","gif","webp","bmp","svg","swf","map","crdownload",
			"aac","avi","mp4","mp3","mdi","csv","doc","docx","xls","xlsx","ppt","pptx","rtf","wav","pdf",
			"otf","tif","ttf","eot","tiff","woff","woff2","7z","rar","tar","zip"
			);//后缀可定制取消
	public final static Map<String,Three<Path,Long,Tuple<CompiledScript,List<String>>>> FILTER_FUNS=new HashMap<>();//过滤器
	
	public final static ScopedValue<Ctx> RQS =  ScopedValue.newInstance();
	public static int existsNot=-1;
	public static boolean upload_auto=true;//自动上传
	public static boolean cors_allow=true;//允许跨域
	public static boolean has_mobile=false;//包含手机目录
	public static String mobile_dir="m";//包含手机目录
	public static String cors_access_control_allow_origin="";
	public static String cors_access_control_allow_methods="";
	public static String cors_access_control_max_age="";
	public static String cors_access_control_allow_headers="";
	public static String cors_access_control_allow_credentials="";
	public static String ipcheck;//检查IP
	public static Object sc;
	public static boolean isStatic(String url,String suffix) {
		if(Lang.startsWith(url, STATIC_PRIFIX)) {
			return true;
		}else if(STATIC_SUFFIX.contains(suffix)) {
			return true;
		}
		return false;
	}
	
	public static boolean filter(Ctx tbl,boolean before) {
		var filtermapping=Lang.startsWithGet(tbl.getPath(),Web.FILTER_FUNS.keySet());
		if(filtermapping!=null) {//拦截器
			var t=Web.FILTER_FUNS.get(filtermapping);
			if(before) {//是前置方法
				var name=t.c().b().contains("before")?"before":"main";
				var r=Js.cFun(t.a(),name,tbl);
				if(r!=null&&r!=Boolean.TRUE)
				try {View.filter.render(tbl,r);return false;} catch (Exception e){throw new RuntimeException(e);}
			}else {
				if(t.c().b().contains("after")) {//是后置方法并且存在 注册时没有则不会执行除非手动添加
					var r=Js.cFun(t.a(),"after",tbl);
					if(r!=null&&r!=Boolean.TRUE)
					try {View.filter.render(tbl,r);return false;} catch (Exception e){throw new RuntimeException(e);}
				}
			}
		}
		return true;
	}
	public static void init() throws Exception {
		if(Jse.webapp()==null) {
			if(Files.exists(Path.of(Jse.dir,"web"))) {
				Jse.setWebapp(Jse.dir+"/web");
				Jse.setWebapps(Jse.dir);
			}
		}
		if(Jse.webapp()==null) {//可能存在未设置webapp等，自动获取下
			Jse.setWebapp(Jse.dir);
			if(Jse.webapp().endsWith("/"))Jse.setWebapp(Jse.webapp().substring(0,Jse.webapp().length()-1));
			Jse.setWebapps(Jse.webapp().substring(0,Jse.webapp().lastIndexOf('/')));
			if(Jse.webapp().contains("tomcat"))Jse.setWebapps(Jse.webapps()+"/webapps");//使用tomcat时的默认webapps
		}
		Jse.app(Web.class,null);
		if(Jse.tempdir==null)Jse.tempdir=new File(Jse.tmpdir);
		if(Jse.conf.containsKey("jse.session.timeout"))setSessionTimeout(Jse.conf.getInt("jse.session.timeout"));//设置超时
		Web.data("root","/");//设置默认root变量 如页面中 #(root)
		
		Js.execute("var Web=Java.type('com.jse.web.Web');");
		has_mobile=Files.exists(Path.of(Jse.webapp(),"m"));
		var JPATH=Path.of(Jse.jspath());
		Files.walkFileTree(JPATH,EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
				new SimpleFileVisitor<Path>() {
					@Override
					public FileVisitResult visitFile(Path p, BasicFileAttributes attrs) throws IOException {
						String f=p.toString().substring(Jse.jspath().length()).replace('\\','/');
						String suffix=Fs.suffix(f);//后缀
						String mapping=f.substring(0,f.length()-suffix.length()-1);
						try {
							if(mapping.startsWith("/$/")&&"js".equals(suffix)) {//过滤器
								String mapping1=mapping.equals("/$/$")?"":mapping.substring(2);
								FILTER_FUNS.put(mapping1+"/",new Three<Path,Long,Tuple<CompiledScript,List<String>>>
								(p,Fs.lastModifiedTime(p),Js.compiles(p)));
							}
						} catch (Exception e2) {
							e2.printStackTrace();
							throw new RuntimeException("编译出错:"+f);
						}
						return super.visitFile(p, attrs);
					}
		});
		String jsejs=Jse.jspath()+"/jse.js";
		if(Fs.exists(jsejs))Js.execute(Fs.read(jsejs));
		log.debug("dir:{}", Jse.dir);
        log.debug("name:{}", Jse.name);
        log.debug("classpath:{}", Jse.classpath);
        log.debug("jspath:{}", Jse.jspath);
        log.debug("webapps:{}", Jse.webapps);
        log.debug("webapp:{}", Jse.webapp);
	}
	
	public final static Map<String,Object> data=new HashMap<>();//servletContent attr
	public static void data(String name,Object val) {data.put(name,val);}
	public static Object data(String name) {return data.get(name);}
	public static void attr(String name,Object val) {RQS.get().setAttribute(name,val);}
	public static Object attr(String name) {return RQS.get().getAttribute(name);}
	public static void sattr(String name,Object val) {RQS.get().sattr(name, val);}
	public static Object sattr(String name) {return RQS.get().sattr(name);}
	static int sessionTimeout=30*60;
	
	private static void setSessionTimeout(Integer timeout) {
		sessionTimeout=timeout;
	}

	private static class App {
        private static Web web = new Web();
    }
	public static Web web() {return App.web;}
	
	public static Map<String, Object> datas(Map<String, Object> map) {
		data.forEach((k,v)->map.putIfAbsent(k, v));
		var req=RQS.get();
		map.put("cache",Jse.cache);
		map.put("req",req);
		map.put("session",req.sessionMap());
		return map;
	}

	public static boolean existsNot() {
		if(existsNot==-1) {
			if(Fs.exists(Jse.webapp()+"/404.html")) {
				existsNot=1;
			}else {
				existsNot=0;
			}
		}
		return existsNot==1;
	}

//	public static boolean isWebSocket(Ctx ex) {//"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
//		return (ex.getRequestHeaders().get("Upgrade").contains("websocket")&&
//                "GET".equals(ex.getMethod()));
//    }

	public static void cors(Ctx tbl) {
	if(cors_allow) {
		tbl.setHeader("Access-Control-Allow-Origin",cors_access_control_allow_origin);
		tbl.setHeader("Access-Control-Allow-Methods",cors_access_control_allow_methods);
		tbl.setHeader("Access-Control-Max-Age",cors_access_control_max_age);
		tbl.setHeader("Access-Control-Allow-Headers",cors_access_control_allow_headers);
		tbl.setHeader("Access-Control-Allow-Credentials",cors_access_control_allow_credentials);
	}
}

public static boolean ipCheck(Ctx tbl) {
	if(Strings.isNotEmpty(ipcheck)&&!IP.checkIP(tbl.getRemoteAddr(),ipcheck)) {
		try {
			View.text.render(tbl, "当前IP不被允许");
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
      }
	return true;
}

public static String mapping(String url,String suffix,Map<String,Object> tbl) {//TODO 暂只支持最后一段path
	String s=url;if(s.endsWith("/"))s+="index";
	s=s.endsWith("."+suffix)?s.substring(0,s.length()-(suffix.length()+1)):s;
	int index=s.lastIndexOf('/');
	var s1=s.substring(index+1);
	if(Strings.isNumber(s1)){
		var id=Integer.valueOf(s1);
		tbl.put("id",id);
		if(suffix.length()>0)return s.substring(0,index);
		return s.substring(0,index)+"/id";
	}
	if(Strings.hasUpperCase(s1)||Strings.hasChinese(s1)){//包含大写或中文
		tbl.put("name",s1);
		if(suffix.length()>0)return s.substring(0,index);
		return s.substring(0,index)+"/name";
	}
	return s;
}

}
